﻿
Imports System.IO
Imports System.Net
Imports System.Net.Sockets
Imports std

Public Class tcp_client
    Public holdtimeMs As UInt32 = 30000
    Private ReadOnly client As TcpClient = Nothing
    Private ReadOnly lock As lockWrapper(Of locks.eventCombLock)
    Private _connected As Boolean = False

    Public Sub New(ByVal client As TcpClient)
        assert(Not client Is Nothing)
        Me.client = client
        _connected = client.Connected()
        lock = New lockWrapper(Of locks.eventCombLock)()
    End Sub

    Public Sub New()
        Me.New(New TcpClient())
    End Sub

    Private Function send(ByVal d() As Byte, ByRef ec As eventComb) As Boolean
        Return pac_trans.send(stream(), d, holdtimeMs, ec)
    End Function

    Private Function receive(ByVal rtn As pointer(Of bytes), ByRef ec As eventComb) As Boolean
        Return pac_trans.receive(stream(), rtn, holdtimeMs, ec)
    End Function

    Private Function work(ByVal inner As eventComb) As eventComb
        assert(Not inner Is Nothing)
        Return New eventComb(Function() As Boolean
                                 Return waitfor(Function() connected(), holdtimeMs) AndAlso goto_next()
                             End Function,
                             Function() As Boolean
                                 Return waitfor(lock) AndAlso goto_next()
                             End Function,
                             Function() As Boolean
                                 Return waitfor(inner) AndAlso goto_next()
                             End Function,
                             Function() As Boolean
                                 lock.release()
                                 Return inner.end_result() AndAlso goto_end()
                             End Function)
    End Function

    Public Sub reply(ByVal d As _do(Of Byte(), Byte(), Boolean), ByRef ec As eventComb)
        assert(Not d Is Nothing)
        reply(Function(ByRef i As Byte(), ByRef po As pointer(Of bytes)) As eventComb
                  Dim o() As Byte = Nothing
                  If d(i, o) Then
                      eva(po, New bytes(o))
                      Return eventComb.succeeded()
                  Else
                      Return eventComb.failed()
                  End If
              End Function,
              ec)
    End Sub

    Public Sub reply(ByVal d As _do(Of Byte(), pointer(Of bytes), eventComb), ByRef ec As eventComb)
        assert(Not d Is Nothing)
        Dim i As pointer(Of bytes) = Nothing
        Dim o As pointer(Of bytes) = Nothing
        Dim rec As eventComb = Nothing
        Dim sec As eventComb = Nothing
        Dim dec As eventComb = Nothing
        ec = work(New eventComb(Function() As Boolean
                                    i = New pointer(Of bytes)()
                                    Return receive(i, rec) AndAlso
                                           waitfor(rec) AndAlso
                                           goto_next()
                                End Function,
                                Function() As Boolean
                                    If rec.end_result() Then
                                        o = New pointer(Of bytes)()
                                        dec = d(++i, o)
                                        Return waitfor(dec) AndAlso
                                               goto_next()
                                    Else
                                        Return False
                                    End If
                                End Function,
                                Function() As Boolean
                                    If dec.end_result() Then
                                        Return send(++o, sec) AndAlso
                                               waitfor(sec) AndAlso
                                               goto_next()
                                    Else
                                        raiseError(errorHandle.errorType.warning,
                                                   "failed to handle input by delegate.")
                                        Return goto_end()
                                    End If
                                End Function,
                                Function() As Boolean
                                    Return sec.end_result() AndAlso goto_end()
                                End Function))
    End Sub

    Public Sub talk(ByVal d() As Byte, ByVal rtn As pointer(Of bytes), ByRef ec As eventComb)
        Dim sec As eventComb = Nothing
        Dim rec As eventComb = Nothing
        ec = work(New eventComb(Function() As Boolean
                                    Return send(d, sec) AndAlso
                                           waitfor(sec) AndAlso
                                           goto_next()
                                End Function,
                                Function() As Boolean
                                    If sec.end_result() Then
                                        Return receive(rtn, rec) AndAlso
                                               waitfor(rec) AndAlso
                                               goto_next()
                                    Else
                                        Return False
                                    End If
                                End Function,
                                Function() As Boolean
                                    Return rec.end_result() AndAlso goto_end()
                                End Function))
    End Sub

    Private Sub bind(ByVal address As vector(Of IPAddress2), ByVal port As Int32)
        _connected = False
        Dim cec As eventComb = Nothing
        Dim result As pointer(Of Boolean) = Nothing
        result = New pointer(Of Boolean)()
        cec = eventCombAsyncOperation.ctor(Function(ByRef a As AsyncCallback, ByRef obj As Object) As IAsyncResult
                                               Return client.BeginConnect(+address, port, a, obj)
                                           End Function,
                                           Function(ByRef a As IAsyncResult) As Boolean
                                               Try
                                                   client.EndConnect(a)
                                                   tcpTrans.initialTcpClient(client)
                                                   Return True
                                               Catch
                                                   Return False
                                               End Try
                                           End Function,
                                           result)
        assert_begin(New eventComb(Function() As Boolean
                                       Return waitfor(lock) AndAlso goto_next()
                                   End Function,
                                   Function() As Boolean
                                       Return waitfor(cec) AndAlso goto_next()
                                   End Function,
                                   Function() As Boolean
                                       If cec.end_result() Then
                                           _connected = +result
                                           lock.release()
                                           Return goto_end()
                                       Else
                                           Return False
                                       End If
                                   End Function))
    End Sub

    Public Function bind(ByVal host As String, ByVal port As Int32) As Boolean
        Dim addresses As vector(Of IPAddress2) = Nothing
        addresses = dnscache.resolve(host)
        If addresses Is Nothing OrElse addresses.size() = 0 Then
            raiseError(errorHandle.errorType.warning, "cannot get ip address for host ", host)
            Return False
        Else
            bind(addresses, port)
            Return True
        End If
    End Function

    Public Function connected() As Boolean
        Return _connected
    End Function

    Public Function stream() As Stream
        Try
            Return client.GetStream()
        Catch
            Return Nothing
        End Try
    End Function

    Protected Overrides Sub Finalize()
        tcpTrans.closeTcpClient(client)
        GC.KeepAlive(client)
        MyBase.Finalize()
    End Sub
End Class
